library(modplots)
Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ─────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.3      ✔ purrr   0.3.4 
✔ tibble  3.1.8      ✔ dplyr   1.0.10
✔ tidyr   1.1.3      ✔ stringr 1.4.0 
✔ readr   1.4.0      ✔ forcats 0.5.1 
── Conflicts ────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
library(Seurat)
Attaching SeuratObject
library(plotly)

Attaching package: ‘plotly’

The following object is masked from ‘package:ggplot2’:

    last_plot

The following object is masked from ‘package:stats’:

    filter

The following object is masked from ‘package:graphics’:

    layout
# Mitochondrial genes
mt <- c("ENSGALG00000035334", #COX3
        "ENSGALG00000032456", #COII
        "ENSGALG00000032142", #MT-CO1
        "ENSGALG00000032079", #MT-CYB
        "ENSGALG00000037838", #ND6
        "ENSGALG00000029500", #ND5
        "ENSGALG00000036229", #MT-ND4
        "ENSGALG00000042478", #ND4L
        "ENSGALG00000030436", #ND3
        "ENSGALG00000041091", #MT-ATP6
        "ENSGALG00000032465", #MT-ATP8
        "ENSGALG00000043768", #MT-ND2
        "ENSGALG00000042750") #MT-ND1

# volcanoplot thresholds
p.adj <- 0.05
l2fc <- 0.5

B10-L10-int

# seurat object
my.se <- readRDS("~/spinal_cord_paper/data/Gg_ctrl_lumb_int_seurat_250723.rds")
# cluster labels from B10int and L10int
ctrl_lumb_int_combined_labels <- readRDS("~/spinal_cord_paper/annotations/ctrl_lumb_int_combined_labels.rds")

identical(colnames(my.se), rownames(ctrl_lumb_int_combined_labels))
[1] TRUE
my.se$annot_sample  <- ctrl_lumb_int_combined_labels$annot_sample

my.se@active.assay <- "RNA"

intra cluster DE analysis

We do DE analysis per cluster, contrasting B10 and L10 samples:

markers <- list()
numbers <- list()
composition <-list()

for (i in seq(levels(Idents(my.se)))) {
  # subset for individual clusters
  mn.se <- subset(x = my.se, idents = levels(Idents(my.se))[i])
  mn.se$sample <- str_extract(mn.se$orig.ident, "ctrl|lumb")
  
  composition[[i]] <- mn.se[[]] %>% 
    select(sample, annot_sample)
  
  Idents(mn.se) <- "sample"
  
  tmp_markers <- FindMarkers(mn.se,
                             ident.1 = "ctrl",
                             only.pos = FALSE, 
                             min.pct = 0.25, 
                             logfc.threshold =  0.2,
                             latent.vars = c("CC.Difference.seurat"), 
                             test.use = "MAST", 
                             assay = "RNA")
  # cell numbers per sample
  numbers[[i]] <- data.frame(table(mn.se$sample))
  
  tmp_markers <- tmp_markers %>%
    rownames_to_column("Gene.stable.ID") %>% 
    left_join(gnames)
  
  markers[[i]] <- tmp_markers
}

names(markers) <- paste0("cl-", levels(Idents(my.se)))
names(numbers) <- paste0("cl-", levels(Idents(my.se)))
names(composition) <- paste0("cl-", levels(Idents(my.se)))

# bind lists into data frames
lumb_markers <- bind_rows(markers, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))
lumb_numbers <- bind_rows(numbers, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))
lumb_composition <- bind_rows(composition, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))

saveRDS(lumb_markers, "~/spinal_cord_paper/data/Gg_ctrl_lumb_int_markers.rds")
saveRDS(lumb_numbers, "~/spinal_cord_paper/data/Gg_ctrl_lumb_int_numbers.rds")
saveRDS(lumb_composition, "~/spinal_cord_paper/data/Gg_ctrl_lumb_int_composition.rds")
lumb_markers <- readRDS("~/spinal_cord_paper/data/Gg_ctrl_lumb_int_markers.rds") %>% 
  mutate(clust_id = str_remove(cluster, "^cl-"))
Error in readRDS("~/spinal_cord_paper/data/Gg_ctrl_lumb_int_markers.rds") %>%  : 
  could not find function "%>%"

Plot the cluster compositions and the number of marker genes.


DimPlot(my.se, label = TRUE, reduction = "tsne")


ggplot(data = lumb_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 3) +
  geom_text(nudge_y = 50, size = 3)


ggplot(data = lumb_markers %>% filter(p_val_adj < 0.05 & avg_log2FC > 0.5), aes(x = cluster, group = cluster)) +
  geom_bar() +
  facet_wrap("cluster", nrow = 3,scales = "free_x")

NA
NA
NA

Neurons

gnames[grepl("TUBB3",gnames$Gene.name),]

DimPlot(my.se, label = TRUE, reduction = "tsne")

# TUBB3 expression
VlnPlot(my.se, features = c("ENSGALG00000000059"), pt.size = 0)


# vector of neuronal clusters
neuron_clusters <- c(3,7,17,20,27,28,30)

neuron_lumb_markers <- filter(lumb_markers, clust_id %in% neuron_clusters)
neuron_lumb_numbers <- filter(lumb_numbers, clust_id %in% neuron_clusters)
neuron_lumb_composition <- filter(lumb_compositions, clust_id %in% neuron_clusters)
Error in filter(lumb_compositions, clust_id %in% neuron_clusters) : 
  object 'lumb_compositions' not found

Barplots

Barplots showing number of cells from B10 or L10, and the contributions from the individual B10int and L10int clusters:

toplot <- neuron_lumb_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) +
    theme(legend.position="none")

Volcanoplots

toplot <- neuron_lumb_markers %>% 
  mutate(gene_type = case_when(
    avg_log2FC >= 0.5 & p_val_adj <= 0.05 ~ "ctrl",
    avg_log2FC <= -0.5 & p_val_adj <= 0.05 ~ "lumb",
    TRUE ~ "ns")
  )

cols <- c(ctrl = "black",
          lumb = "#419c73",
          ns = "grey")

shapes <- c(ctrl = 21,
            lumb = 21,
            ns = 20)

volplot <- ggplot(data = toplot,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 2, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot, width = 1000, height = 600)
NA

Plots without HOX and mitochondrial genes:

# filter out HOX and MT genes
toplot_nomt <- toplot %>% 
  filter(!grepl("^HOX", Gene.name)) %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 2, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt, width = 1000, height = 600)
NA
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 6096 rows containing missing values (geom_text_repel).
Warning: ggrepel: 48 unlabeled data points (too many overlaps). Consider increasing max.overlaps
pdf("~/spinal_cord_paper/figures/Fig_4_neuron_volcano.pdf", width = 10, height = 7)
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 6096 rows containing missing values (geom_text_repel).
Warning: ggrepel: 43 unlabeled data points (too many overlaps). Consider increasing max.overlaps
dev.off()
png 
  2 

No MN DE genes

Cluster 30 (Consisting of 161 B10 vs 13 L10 cells) shows no MN related markers. Seemingly, those 13 cells are indeed motor neurons. This is supported by the fact of their expression of TUBB3, FOXP1, and SLC18A3.

MFOLs

gnames[grepl("^PLP1$|^MBP$|^FABP9$",gnames$Gene.name),]

DimPlot(my.se, label = TRUE, reduction = "tsne")

# MFOL marker expression
VlnPlot(my.se, features = c("ENSGALG00000000112","ENSGALG00000013640","ENSGALG00000032453"), pt.size = 0, ncol = 1)


# vector of neuronal clusters
MFOL_clusters <- c(16, 18, 34)

MFOL_lumb_markers <- filter(lumb_markers, clust_id %in% MFOL_clusters)
MFOL_lumb_numbers <- filter(lumb_numbers, clust_id %in% MFOL_clusters)
MFOL_lumb_composition <- filter(lumb_composition, clust_id %in% MFOL_clusters)

Barplots

Barplots showing number of cells from B10 or L10, and the contributions from the individual B10int and L10int clusters:

ggplot(data = MFOL_lumb_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 1) +
  geom_text(nudge_y = 10)

toplot <- MFOL_lumb_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) +
    theme(legend.position="none")

Volcanoplots

toplot <- MFOL_lumb_markers %>% 
  mutate(gene_type = case_when(
    avg_log2FC >= 0.5 & p_val_adj <= 0.05 ~ "ctrl",
    avg_log2FC <= -0.5 & p_val_adj <= 0.05 ~ "lumb",
    TRUE ~ "ns")
  )

cols <- c(ctrl = "black",
          lumb = "#419c73",
          ns = "grey")

shapes <- c(ctrl = 21,
            lumb = 21,
            ns = 20)

volplot <- ggplot(data = toplot,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot, width = 800, height = 500)
NA

Plots without HOX and mitochondrial genes:

# filter out HOX and MT genes
toplot_nomt <- toplot %>% 
  filter(!grepl("^HOX", Gene.name)) %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt, width = 800, height = 500)
NA
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 2944 rows containing missing values (geom_text_repel).
pdf("~/spinal_cord_paper/figures/Fig_4_MFOL_volcano.pdf", width = 10, height = 7)
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 2944 rows containing missing values (geom_text_repel).
dev.off()
png 
  2 

B10-P10-int

# seurat object
my.se <- readRDS("~/spinal_cord_paper/data/Gg_ctrl_poly_int_seurat_250723.rds")
# cluster labels from B10int and L10int
ctrl_poly_int_combined_labels <- readRDS("~/spinal_cord_paper/annotations/ctrl_poly_int_combined_labels.rds")

identical(colnames(my.se), rownames(ctrl_poly_int_combined_labels))
[1] TRUE
my.se$annot_sample  <- ctrl_poly_int_combined_labels$annot_sample

my.se@active.assay <- "RNA"

intra cluster DE analysis

We do DE analysis per cluster, contrasting B10 and P10 samples:

markers <- list()
numbers <- list()
composition <- list()

for (i in seq(levels(Idents(my.se)))) {
  # subset for individual clusters
  mn.se <- subset(x = my.se, idents = levels(Idents(my.se))[i])
  mn.se$sample <- str_extract(mn.se$orig.ident, "ctrl|poly")
  
  composition[[i]] <- mn.se[[]] %>% 
    select(sample, annot_sample)
  
  Idents(mn.se) <- "sample"
  
  tmp_markers <- FindMarkers(mn.se,
                             ident.1 = "ctrl",
                             only.pos = FALSE, 
                             min.pct = 0.25, 
                             logfc.threshold =  0.2,
                             latent.vars = c("CC.Difference.seurat"), 
                             test.use = "MAST", 
                             assay = "RNA")
  # cell numbers per sample
  numbers[[i]] <- data.frame(table(mn.se$sample))
  
  tmp_markers <- tmp_markers %>%
    rownames_to_column("Gene.stable.ID") %>% 
    left_join(gnames)
  
  
  markers[[i]] <- tmp_markers
}

names(markers) <- paste0("cl-", levels(Idents(my.se)))
names(numbers) <- paste0("cl-", levels(Idents(my.se)))
names(composition) <- paste0("cl-", levels(Idents(my.se)))

# bind lists into data frames
poly_markers <- bind_rows(markers, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))
poly_numbers <- bind_rows(numbers, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))
poly_composition <- bind_rows(composition, .id = "cluster") %>% 
  mutate(cluster = factor(cluster, levels = paste0("cl-", levels(Idents(my.se)))))

saveRDS(poly_markers, "~/spinal_cord_paper/data/Gg_ctrl_poly_int_markers.rds")
saveRDS(poly_numbers, "~/spinal_cord_paper/data/Gg_ctrl_poly_int_numbers.rds")
saveRDS(poly_composition, "~/spinal_cord_paper/data/Gg_ctrl_poly_int_composition.rds")
# load the DE data
poly_markers <- readRDS("~/test/poly_markers.rds") %>% 
  mutate(clust_id = str_remove(cluster, "^cl-"))
poly_numbers <- readRDS("~/test/poly_numbers.rds") %>% 
  mutate(clust_id = str_remove(cluster, "^cl-"))
poly_composition <- readRDS("~/test/poly_composition.rds") %>% 
  mutate(clust_id = str_remove(cluster, "^cl-"))

Plot the cluster compositions and the number of marker genes.


DimPlot(my.se, label = TRUE, reduction = "tsne")


ggplot(data = poly_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 3) +
  geom_text(nudge_y = 50, size = 3)


ggplot(data = poly_markers %>% filter(p_val_adj < 0.05 & avg_log2FC > 0.5), aes(x = cluster, group = cluster)) +
  geom_bar() +
  facet_wrap("cluster", nrow = 3,scales = "free_x")

Neurons

gnames[grepl("TUBB3",gnames$Gene.name),]

# TUBB3 expression
VlnPlot(my.se, features = c("ENSGALG00000000059"), pt.size = 0)


# vector of neuronal clusters
neuron_clusters <- c(5,6,11,20,25,27,29,30,32)

neuron_poly_markers <- filter(poly_markers, clust_id %in% neuron_clusters)
neuron_poly_numbers <- filter(poly_numbers, clust_id %in% neuron_clusters)
neuron_poly_composition <- filter(poly_composition, clust_id %in% neuron_clusters)

Barplots

Barplots showing number of cells from B10 or P10, and the contributions from the individual B10int and P10int clusters:

ggplot(data = neuron_poly_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 1) +
  geom_text(nudge_y = 10)


toplot <- neuron_poly_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) + 
    theme(legend.position="none")

Volcanoplots

toplot <- neuron_poly_markers %>% 
  mutate(gene_type = case_when(
    avg_log2FC >= 0.5 & p_val_adj <= 0.05 ~ "ctrl",
    avg_log2FC <= -0.5 & p_val_adj <= 0.05 ~ "poly",
    TRUE ~ "ns")
  )

cols <- c(ctrl = "black",
          poly = "goldenrod3",
          ns = "grey")

shapes <- c(ctrl = 21,
            poly = 21,
            ns = 20)


volplot <- ggplot(data = toplot,
                       aes(x = avg_log2FC,
                           y = -log10(p_val_adj),
                           label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                       )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 2, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot, width = 1000, height = 600)

Plots without mitochondrial genes:

toplot_nomt <- toplot %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                       aes(x = avg_log2FC,
                           y = -log10(p_val_adj),
                           label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                       )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 2, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt)
volplot_nomt  +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 6087 rows containing missing values (geom_text_repel).
pdf("~/spinal_cord_paper/figures/Fig_5_neuron_volcano.pdf", width = 10, height = 7)
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 6087 rows containing missing values (geom_text_repel).
dev.off()
png 
  2 

Single plot (no faceting due to low number of DE genes).

volplot_nomt_ind <- ggplot(data = toplot_nomt,
                       aes(x = avg_log2FC,
                           y = -log10(p_val_adj),
                           label = Gene.name,
                           color = gene_type,
                           shape = cluster,
                       )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = c("black", "grey", "goldenrod3"))+
  scale_shape_manual(values = c(0, 16, 2, 3, 5, 16, 16, 16, 16)) +
  xlim(c(-1,1)) +
  ylab("-log10(padj)") +
  theme_bw() +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")

volplot_nomt_ind
Warning: Removed 10 rows containing missing values (geom_point).
Warning: Removed 6087 rows containing missing values (geom_text_repel).

MFOLs

gnames[grepl("^PLP1$|^MBP$|^FABP9$",gnames$Gene.name),]

# MFOL marker expression
VlnPlot(my.se, features = c("ENSGALG00000000112","ENSGALG00000013640","ENSGALG00000032453"), pt.size = 0, ncol = 1)


# vector of neuronal clusters
MFOL_clusters <- c(10, 24)

MFOL_poly_markers <- filter(poly_markers, clust_id %in% MFOL_clusters)
MFOL_poly_numbers <- filter(poly_numbers, clust_id %in% MFOL_clusters)
MFOL_poly_composition <- filter(poly_composition, clust_id %in% MFOL_clusters)

Barplots

Barplots showing number of cells from B10 or L10, and the contributions from the individual B10int and L10int clusters:

ggplot(data = MFOL_poly_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 1) +
  geom_text(nudge_y = 10)

toplot <- MFOL_poly_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) +
    theme(legend.position="none")

Volcanoplots

Plots without HOX and mitochondrial genes:

# filter out HOX and MT genes
toplot_nomt <- toplot %>% 
  filter(!grepl("^HOX", Gene.name)) %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt, width = 800, height = 500)
NA
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 1 rows containing missing values (geom_point).
Warning: Removed 406 rows containing missing values (geom_text_repel).
pdf("~/spinal_cord_paper/figures/Fig_5_MFOL_volcano.pdf", width = 10, height = 7)
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 1 rows containing missing values (geom_point).
Warning: Removed 406 rows containing missing values (geom_text_repel).
dev.off()
png 
  2 

RP and FP

gnames[grepl("^RSPO1$|^SHH$",gnames$Gene.name),]

# MFOL marker expression
VlnPlot(my.se, features = c("ENSGALG00000001946","ENSGALG00000006379"), pt.size = 0, ncol = 1)


# vector of neuronal clusters
RPFP_clusters <- c(22, 23)

RPFP_poly_markers <- filter(poly_markers, clust_id %in% RPFP_clusters)
RPFP_poly_numbers <- filter(poly_numbers, clust_id %in% RPFP_clusters)
RPFP_poly_composition <- filter(poly_composition, clust_id %in% RPFP_clusters)

Barplots

Barplots showing number of cells from B10 or L10, and the contributions from the individual B10int and L10int clusters:

ggplot(data = RPFP_poly_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 1) +
  geom_text(nudge_y = 10)

toplot <- RPFP_poly_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) +
    theme(legend.position="none")

Volcanoplots

toplot <- RPFP_poly_markers %>% 
  mutate(gene_type = case_when(
    avg_log2FC >= 0.5 & p_val_adj <= 0.05 ~ "ctrl",
    avg_log2FC <= -0.5 & p_val_adj <= 0.05 ~ "poly",
    TRUE ~ "ns")
  )

cols <- c(ctrl = "black",
          poly = "goldenrod3",
          ns = "grey")

shapes <- c(ctrl = 21,
            poly = 21,
            ns = 20)

volplot <- ggplot(data = toplot,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot, width = 800, height = 500)
NA

Plots without HOX and mitochondrial genes:

# filter out HOX and MT genes
toplot_nomt <- toplot %>% 
  filter(!grepl("^HOX", Gene.name)) %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt, width = 800, height = 500)
NA
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")

pdf("~/spinal_cord_paper/figures/Fig_5_RPFP_volcano.pdf", width = 5, height = 3.5)
volplot_nomt +
  NoLegend() +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
BP_RPFP_barplot
dev.off()

NPC cl-16 and cl-18

Clusters 16 and 18 show the highest numbers of DE genes. They are a progenitor population

# vector of neuronal clusters
NPC_clusters <- c(16, 18)

NPC_poly_markers <- filter(poly_markers, clust_id %in% NPC_clusters)
NPC_poly_numbers <- filter(poly_numbers, clust_id %in% NPC_clusters)
NPC_poly_composition <- filter(poly_composition, clust_id %in% NPC_clusters)

Barplots

Barplots showing number of cells from B10 or L10, and the contributions from the individual B10int and L10int clusters:

ggplot(data = NPC_poly_numbers, aes(x = Var1, y = Freq, group = cluster, label = Freq)) +
  geom_col() +
  facet_wrap("cluster", nrow = 1) +
  geom_text(nudge_y = 10)

toplot <- NPC_poly_composition %>% 
  mutate(annot_sample = str_extract(annot_sample, "\\d{1,2}_.{1}")) %>% 
  mutate(annot_sample = factor(annot_sample)) %>% 
  group_by(sample, cluster) %>%
  count(annot_sample) %>% 
  mutate(label_ypos = cumsum(n)- 0.5*n)

ggplot(data=toplot, aes(x=sample, y=n, fill=fct_rev(annot_sample))) +
  geom_bar(stat="identity", color = "black") +
  geom_text(aes(y=label_ypos, label=annot_sample), 
            color="black", size=3.5) +
  facet_wrap("cluster", nrow = 1) +
    theme(legend.position="none")

Volcanoplots

toplot <- NPC_poly_markers %>% 
  mutate(gene_type = case_when(
    avg_log2FC >= 0.5 & p_val_adj <= 0.05 ~ "ctrl",
    avg_log2FC <= -0.5 & p_val_adj <= 0.05 ~ "poly",
    TRUE ~ "ns")
  )

cols <- c(ctrl = "black",
          poly = "goldenrod3",
          ns = "grey")

shapes <- c(ctrl = 21,
            poly = 21,
            ns = 20)

volplot <- ggplot(data = toplot,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot, width = 800, height = 500)
NA

Plots without HOX and mitochondrial genes:

# filter out HOX and MT genes
toplot_nomt <- toplot %>% 
  filter(!grepl("^HOX", Gene.name)) %>% 
  filter(!Gene.stable.ID %in% mt)

volplot_nomt <- ggplot(data = toplot_nomt,
                  aes(x = avg_log2FC,
                      y = -log10(p_val_adj),
                      label = Gene.name,
                      color = gene_type,
                      shape = gene_type
                  )) +
  geom_point() +
  geom_hline(yintercept = -log10(p.adj), linetype = "dashed") +
  geom_vline(xintercept = c(-l2fc,l2fc), linetype = "dashed") +
  scale_color_manual(values = cols) +
  scale_shape_manual(values = shapes) +
  facet_wrap("cluster", nrow = 1, scales = "free") +
  ylab("-log10(padj)") +
  theme_bw()

ggplotly(volplot_nomt, width = 800, height = 500)
NA
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 1899 rows containing missing values (geom_text_repel).
Warning: ggrepel: 43 unlabeled data points (too many overlaps). Consider increasing max.overlaps
Warning: ggrepel: 135 unlabeled data points (too many overlaps). Consider increasing max.overlaps
pdf("~/spinal_cord_paper/figures/Fig_5_NPC_volcano.pdf", width = 10, height = 7)
volplot_nomt +
    ggrepel::geom_text_repel(aes(label = ifelse(gene_type == 'ns',
                                                NA,
                                                Gene.name)),
                             size = 3,
                             color = "black")
Warning: Removed 1899 rows containing missing values (geom_text_repel).
Warning: ggrepel: 17 unlabeled data points (too many overlaps). Consider increasing max.overlaps
Warning: ggrepel: 124 unlabeled data points (too many overlaps). Consider increasing max.overlaps
dev.off()
png 
  2 

# Date and time of Rendering
Sys.time()

sessionInfo()
LS0tCnRpdGxlOiAiQWx0ZXJuYXRpdmUgQjEwLUwxMC1pbnQgYW5kIEIxMC1QMTAtaW50IERFIChuZXVyb25zKSIKYXV0aG9yOiAiRmFiaW8gU2FjaGVyIgpkYXRlOiAiMDMuMDkuMjAyNCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IFRSVUUKICAgIHRvY19mbG9hdDogVFJVRQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgaHRtbF9ub3RlYm9vazoKICAgIGZpZ19oZWlnaHQ6IDcKICAgIGZpZ193aWR0aDogOAplZGl0b3Jfb3B0aW9uczoKICBjaHVua19vdXRwdXRfdHlwZTogaW5saW5lCi0tLQoKYGBge3IgbGlicmFyaWVzfQpsaWJyYXJ5KG1vZHBsb3RzKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkocGxvdGx5KQpgYGAKCmBgYHtyIHNldHVwfQojIE1pdG9jaG9uZHJpYWwgZ2VuZXMKbXQgPC0gYygiRU5TR0FMRzAwMDAwMDM1MzM0IiwgI0NPWDMKICAgICAgICAiRU5TR0FMRzAwMDAwMDMyNDU2IiwgI0NPSUkKICAgICAgICAiRU5TR0FMRzAwMDAwMDMyMTQyIiwgI01ULUNPMQogICAgICAgICJFTlNHQUxHMDAwMDAwMzIwNzkiLCAjTVQtQ1lCCiAgICAgICAgIkVOU0dBTEcwMDAwMDAzNzgzOCIsICNORDYKICAgICAgICAiRU5TR0FMRzAwMDAwMDI5NTAwIiwgI05ENQogICAgICAgICJFTlNHQUxHMDAwMDAwMzYyMjkiLCAjTVQtTkQ0CiAgICAgICAgIkVOU0dBTEcwMDAwMDA0MjQ3OCIsICNORDRMCiAgICAgICAgIkVOU0dBTEcwMDAwMDAzMDQzNiIsICNORDMKICAgICAgICAiRU5TR0FMRzAwMDAwMDQxMDkxIiwgI01ULUFUUDYKICAgICAgICAiRU5TR0FMRzAwMDAwMDMyNDY1IiwgI01ULUFUUDgKICAgICAgICAiRU5TR0FMRzAwMDAwMDQzNzY4IiwgI01ULU5EMgogICAgICAgICJFTlNHQUxHMDAwMDAwNDI3NTAiKSAjTVQtTkQxCgojIHZvbGNhbm9wbG90IHRocmVzaG9sZHMKcC5hZGogPC0gMC4wNQpsMmZjIDwtIDAuNQpgYGAKCiMgQjEwLUwxMC1pbnQKCmBgYHtyIEIxMC1MMTAtaW50fQojIHNldXJhdCBvYmplY3QKbXkuc2UgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfbHVtYl9pbnRfc2V1cmF0XzI1MDcyMy5yZHMiKQojIGNsdXN0ZXIgbGFiZWxzIGZyb20gQjEwaW50IGFuZCBMMTBpbnQKY3RybF9sdW1iX2ludF9jb21iaW5lZF9sYWJlbHMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9hbm5vdGF0aW9ucy9jdHJsX2x1bWJfaW50X2NvbWJpbmVkX2xhYmVscy5yZHMiKQoKaWRlbnRpY2FsKGNvbG5hbWVzKG15LnNlKSwgcm93bmFtZXMoY3RybF9sdW1iX2ludF9jb21iaW5lZF9sYWJlbHMpKQpteS5zZSRhbm5vdF9zYW1wbGUgIDwtIGN0cmxfbHVtYl9pbnRfY29tYmluZWRfbGFiZWxzJGFubm90X3NhbXBsZQoKbXkuc2VAYWN0aXZlLmFzc2F5IDwtICJSTkEiCgpgYGAKCiMjIGludHJhIGNsdXN0ZXIgREUgYW5hbHlzaXMKCldlIGRvIERFIGFuYWx5c2lzIHBlciBjbHVzdGVyLCBjb250cmFzdGluZyBCMTAgYW5kIEwxMCBzYW1wbGVzOgoKYGBge3IgaW50cmEtY2x1c3Rlci1ERS1MMTAsIGV2YWw9RkFMU0V9Cm1hcmtlcnMgPC0gbGlzdCgpCm51bWJlcnMgPC0gbGlzdCgpCmNvbXBvc2l0aW9uIDwtbGlzdCgpCgpmb3IgKGkgaW4gc2VxKGxldmVscyhJZGVudHMobXkuc2UpKSkpIHsKICAjIHN1YnNldCBmb3IgaW5kaXZpZHVhbCBjbHVzdGVycwogIG1uLnNlIDwtIHN1YnNldCh4ID0gbXkuc2UsIGlkZW50cyA9IGxldmVscyhJZGVudHMobXkuc2UpKVtpXSkKICBtbi5zZSRzYW1wbGUgPC0gc3RyX2V4dHJhY3QobW4uc2Ukb3JpZy5pZGVudCwgImN0cmx8bHVtYiIpCiAgCiAgY29tcG9zaXRpb25bW2ldXSA8LSBtbi5zZVtbXV0gJT4lIAogICAgc2VsZWN0KHNhbXBsZSwgYW5ub3Rfc2FtcGxlKQogIAogIElkZW50cyhtbi5zZSkgPC0gInNhbXBsZSIKICAKICB0bXBfbWFya2VycyA8LSBGaW5kTWFya2Vycyhtbi5zZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZGVudC4xID0gImN0cmwiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9ubHkucG9zID0gRkFMU0UsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1pbi5wY3QgPSAwLjI1LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsb2dmYy50aHJlc2hvbGQgPSAgMC4yLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxhdGVudC52YXJzID0gYygiQ0MuRGlmZmVyZW5jZS5zZXVyYXQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGVzdC51c2UgPSAiTUFTVCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGFzc2F5ID0gIlJOQSIpCiAgIyBjZWxsIG51bWJlcnMgcGVyIHNhbXBsZQogIG51bWJlcnNbW2ldXSA8LSBkYXRhLmZyYW1lKHRhYmxlKG1uLnNlJHNhbXBsZSkpCiAgCiAgdG1wX21hcmtlcnMgPC0gdG1wX21hcmtlcnMgJT4lCiAgICByb3duYW1lc190b19jb2x1bW4oIkdlbmUuc3RhYmxlLklEIikgJT4lIAogICAgbGVmdF9qb2luKGduYW1lcykKICAKICBtYXJrZXJzW1tpXV0gPC0gdG1wX21hcmtlcnMKfQoKbmFtZXMobWFya2VycykgPC0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpCm5hbWVzKG51bWJlcnMpIDwtIHBhc3RlMCgiY2wtIiwgbGV2ZWxzKElkZW50cyhteS5zZSkpKQpuYW1lcyhjb21wb3NpdGlvbikgPC0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpCgojIGJpbmQgbGlzdHMgaW50byBkYXRhIGZyYW1lcwpsdW1iX21hcmtlcnMgPC0gYmluZF9yb3dzKG1hcmtlcnMsIC5pZCA9ICJjbHVzdGVyIikgJT4lIAogIG11dGF0ZShjbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXIsIGxldmVscyA9IHBhc3RlMCgiY2wtIiwgbGV2ZWxzKElkZW50cyhteS5zZSkpKSkpCmx1bWJfbnVtYmVycyA8LSBiaW5kX3Jvd3MobnVtYmVycywgLmlkID0gImNsdXN0ZXIiKSAlPiUgCiAgbXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlciwgbGV2ZWxzID0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpKSkKbHVtYl9jb21wb3NpdGlvbiA8LSBiaW5kX3Jvd3MoY29tcG9zaXRpb24sIC5pZCA9ICJjbHVzdGVyIikgJT4lIAogIG11dGF0ZShjbHVzdGVyID0gZmFjdG9yKGNsdXN0ZXIsIGxldmVscyA9IHBhc3RlMCgiY2wtIiwgbGV2ZWxzKElkZW50cyhteS5zZSkpKSkpCgpzYXZlUkRTKGx1bWJfbWFya2VycywgIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX2x1bWJfaW50X21hcmtlcnMucmRzIikKc2F2ZVJEUyhsdW1iX251bWJlcnMsICJ+L3NwaW5hbF9jb3JkX3BhcGVyL2RhdGEvR2dfY3RybF9sdW1iX2ludF9udW1iZXJzLnJkcyIpCnNhdmVSRFMobHVtYl9jb21wb3NpdGlvbiwgIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX2x1bWJfaW50X2NvbXBvc2l0aW9uLnJkcyIpCmBgYAoKYGBge3J9CiMgbG9hZCB0aGUgREUgZGF0YQpsdW1iX21hcmtlcnMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfbHVtYl9pbnRfbWFya2Vycy5yZHMiKSAlPiUgCiAgbXV0YXRlKGNsdXN0X2lkID0gc3RyX3JlbW92ZShjbHVzdGVyLCAiXmNsLSIpKQpsdW1iX251bWJlcnMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfbHVtYl9pbnRfbnVtYmVycy5yZHMiKSAlPiUgCiAgbXV0YXRlKGNsdXN0X2lkID0gc3RyX3JlbW92ZShjbHVzdGVyLCAiXmNsLSIpKQpsdW1iX2NvbXBvc2l0aW9uIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX2x1bWJfaW50X2NvbXBvc2l0aW9uLnJkcyIpICU+JSAKICBtdXRhdGUoY2x1c3RfaWQgPSBzdHJfcmVtb3ZlKGNsdXN0ZXIsICJeY2wtIikpCmBgYAoKUGxvdCB0aGUgY2x1c3RlciBjb21wb3NpdGlvbnMgYW5kIHRoZSBudW1iZXIgb2YgbWFya2VyIGdlbmVzLgoKYGBge3J9CgpEaW1QbG90KG15LnNlLCBsYWJlbCA9IFRSVUUsIHJlZHVjdGlvbiA9ICJ0c25lIikKCmdncGxvdChkYXRhID0gbHVtYl9udW1iZXJzLCBhZXMoeCA9IFZhcjEsIHkgPSBGcmVxLCBncm91cCA9IGNsdXN0ZXIsIGxhYmVsID0gRnJlcSkpICsKICBnZW9tX2NvbCgpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDMpICsKICBnZW9tX3RleHQobnVkZ2VfeSA9IDUwLCBzaXplID0gMykgKwogIGdndGl0bGUoIkNsdXN0ZXIgY29tcG9zaXRpb24gYnkgc2FtcGxlIChuQ2VsbHMpIikKCmdncGxvdChkYXRhID0gbHVtYl9tYXJrZXJzICU+JSBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2cyRkMgPiAwLjUpLCBhZXMoeCA9IGNsdXN0ZXIsIGdyb3VwID0gY2x1c3RlcikpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDMsc2NhbGVzID0gImZyZWVfeCIpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2Ygc2lnLiBERSBnZW5lcyIpCgoKCmBgYAoKIyMgTmV1cm9ucwoKYGBge3J9CmduYW1lc1tncmVwbCgiVFVCQjMiLGduYW1lcyRHZW5lLm5hbWUpLF0KCiMgVFVCQjMgZXhwcmVzc2lvbgpWbG5QbG90KG15LnNlLCBmZWF0dXJlcyA9IGMoIkVOU0dBTEcwMDAwMDAwMDA1OSIpLCBwdC5zaXplID0gMCkKCiMgdmVjdG9yIG9mIG5ldXJvbmFsIGNsdXN0ZXJzCm5ldXJvbl9jbHVzdGVycyA8LSBjKDMsNywxNywyMCwyNywyOCwzMCkKCm5ldXJvbl9sdW1iX21hcmtlcnMgPC0gZmlsdGVyKGx1bWJfbWFya2VycywgY2x1c3RfaWQgJWluJSBuZXVyb25fY2x1c3RlcnMpCm5ldXJvbl9sdW1iX251bWJlcnMgPC0gZmlsdGVyKGx1bWJfbnVtYmVycywgY2x1c3RfaWQgJWluJSBuZXVyb25fY2x1c3RlcnMpCm5ldXJvbl9sdW1iX2NvbXBvc2l0aW9uIDwtIGZpbHRlcihsdW1iX2NvbXBvc2l0aW9uLCBjbHVzdF9pZCAlaW4lIG5ldXJvbl9jbHVzdGVycykKYGBgCgoKIyMjIEJhcnBsb3RzCgpCYXJwbG90cyBzaG93aW5nIG51bWJlciBvZiBjZWxscyBmcm9tIEIxMCBvciBMMTAsIGFuZCB0aGUgY29udHJpYnV0aW9ucyBmcm9tIHRoZSBpbmRpdmlkdWFsIEIxMGludCBhbmQgTDEwaW50IGNsdXN0ZXJzOgoKYGBge3IgYmFycGxvdHMtTDEwfQpnZ3Bsb3QoZGF0YSA9IG5ldXJvbl9sdW1iX251bWJlcnMsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gY2x1c3RlciwgbGFiZWwgPSBGcmVxKSkgKwogIGdlb21fY29sKCkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKwogIGdlb21fdGV4dChudWRnZV95ID0gMTApICsKICBnZ3RpdGxlKCJDbHVzdGVyIGNvbXBvc2l0aW9uIGJ5IHNhbXBsZSAobkNlbGxzKSIpCgpgYGAKCmBgYHtyfQp0b3Bsb3QgPC0gbmV1cm9uX2x1bWJfY29tcG9zaXRpb24gJT4lIAogIG11dGF0ZShhbm5vdF9zYW1wbGUgPSBzdHJfZXh0cmFjdChhbm5vdF9zYW1wbGUsICJcXGR7MSwyfV8uezF9IikpICU+JSAKICBtdXRhdGUoYW5ub3Rfc2FtcGxlID0gZmFjdG9yKGFubm90X3NhbXBsZSkpICU+JSAKICBncm91cF9ieShzYW1wbGUsIGNsdXN0ZXIpICU+JQogIGNvdW50KGFubm90X3NhbXBsZSkgJT4lIAogIG11dGF0ZShsYWJlbF95cG9zID0gY3Vtc3VtKG4pLSAwLjUqbikKCkJMX25ldXJvbl9iYXJwbG90IDwtIGdncGxvdChkYXRhPXRvcGxvdCwgYWVzKHg9c2FtcGxlLCB5PW4sIGZpbGw9ZmN0X3Jldihhbm5vdF9zYW1wbGUpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyh5PWxhYmVsX3lwb3MsIGxhYmVsPWFubm90X3NhbXBsZSksIAogICAgICAgICAgICBjb2xvcj0iYmxhY2siLCBzaXplPTMuNSkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikgKwogIGdndGl0bGUoIkZyb20gd2hpY2ggY2x1c3RlcnMgZG8gY2VsbHMgY29tZT8iKQoKQkxfbmV1cm9uX2JhcnBsb3QgCmBgYAoKCiMjIyBWb2xjYW5vcGxvdHMKCmBgYHtyIHZvbHBsb3RzLUwxMCwgZmlnLndpZHRoID0gMTAsIGZpZy5oZWlnaHQgPSA2fQp0b3Bsb3QgPC0gbmV1cm9uX2x1bWJfbWFya2VycyAlPiUgCiAgbXV0YXRlKGdlbmVfdHlwZSA9IGNhc2Vfd2hlbigKICAgIGF2Z19sb2cyRkMgPj0gMC41ICYgcF92YWxfYWRqIDw9IDAuMDUgfiAiY3RybCIsCiAgICBhdmdfbG9nMkZDIDw9IC0wLjUgJiBwX3ZhbF9hZGogPD0gMC4wNSB+ICJsdW1iIiwKICAgIFRSVUUgfiAibnMiKQogICkKCmNvbHMgPC0gYyhjdHJsID0gImJsYWNrIiwKICAgICAgICAgIGx1bWIgPSAiIzQxOWM3MyIsCiAgICAgICAgICBucyA9ICJncmV5IikKCnNoYXBlcyA8LSBjKGN0cmwgPSAyMSwKICAgICAgICAgICAgbHVtYiA9IDIxLAogICAgICAgICAgICBucyA9IDIwKQoKdm9scGxvdCA8LSBnZ3Bsb3QoZGF0YSA9IHRvcGxvdCwKICAgICAgICAgICAgICAgICAgYWVzKHggPSBhdmdfbG9nMkZDLAogICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGdlbmVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZV90eXBlCiAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwLmFkaiksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC1sMmZjLGwyZmMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVzKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAyLCBzY2FsZXMgPSAiZnJlZSIpICsKICB5bGFiKCItbG9nMTAocGFkaikiKSArCiAgdGhlbWVfYncoKSsKICBnZ3RpdGxlKCJTaWcuIG1hcmtlciBnZW5lcyIpCgpnZ3Bsb3RseSh2b2xwbG90LCB3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDYwMCkKCmBgYAoKUGxvdHMgd2l0aG91dCBIT1ggYW5kIG1pdG9jaG9uZHJpYWwgZ2VuZXM6CgpgYGB7ciwgIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KIyBmaWx0ZXIgb3V0IEhPWCBhbmQgTVQgZ2VuZXMKdG9wbG90X25vbXQgPC0gdG9wbG90ICU+JSAKICBmaWx0ZXIoIWdyZXBsKCJeSE9YIiwgR2VuZS5uYW1lKSkgJT4lIAogIGZpbHRlcighR2VuZS5zdGFibGUuSUQgJWluJSBtdCkKCnZvbHBsb3Rfbm9tdCA8LSBnZ3Bsb3QoZGF0YSA9IHRvcGxvdF9ub210LAogICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzICh3aXRob3V0IE1UIGFuZCBIT1ggZ2VuZXMiKQoKZ2dwbG90bHkodm9scGxvdF9ub210LCB3aWR0aCA9IDEwMDAsIGhlaWdodCA9IDYwMCkKCmBgYAoKYGBge3J9CnZvbHBsb3Rfbm9tdCArCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gaWZlbHNlKGdlbmVfdHlwZSA9PSAnbnMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5uYW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzRfbmV1cm9uX3ZvbGNhbm8ucGRmIiwgd2lkdGggPSA5LCBoZWlnaHQgPSA3KQp2b2xwbG90X25vbXQgKwogIE5vTGVnZW5kKCkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShnZW5lX3R5cGUgPT0gJ25zJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUubmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKQkxfbmV1cm9uX2JhcnBsb3QgCmRldi5vZmYoKQpgYGAKCgojIyMgTm8gTU4gREUgZ2VuZXMKCkNsdXN0ZXIgMzAgKENvbnNpc3Rpbmcgb2YgMTYxIEIxMCB2cyAxMyBMMTAgY2VsbHMpIHNob3dzIG5vIE1OIHJlbGF0ZWQgbWFya2Vycy4gU2VlbWluZ2x5LCB0aG9zZSAxMyBjZWxscyBhcmUgaW5kZWVkIG1vdG9yIG5ldXJvbnMuIFRoaXMgaXMgc3VwcG9ydGVkIGJ5IHRoZSBmYWN0IG9mIHRoZWlyIGV4cHJlc3Npb24gb2YgVFVCQjMsIEZPWFAxLCBhbmQgU0xDMThBMy4KCmBgYHtyfQojIE1vdG9yIG5ldXJvbiBjbHVzdGVyCm1uLnNlIDwtIHN1YnNldCh4ID0gbXkuc2UsIGlkZW50cyA9IDMwKQptbi5zZSRzYW1wbGUgPC0gc3RyX2V4dHJhY3QobW4uc2Ukb3JpZy5pZGVudCwgImN0cmx8bHVtYiIpCgptbi5zZUBhY3RpdmUuYXNzYXkgPC0gImludGVncmF0ZWQiCgptbl9tYXJrZXJzIDwtIGduYW1lc1tncmVwbCgiVFVCQjN8Rk9YUDF8U0xDMThBMyIsIGduYW1lcyRHZW5lLm5hbWUpLF0KbW5fbWFya2VycwoKVmxuUGxvdChtbi5zZSwgZ3JvdXAuYnkgPSAic2FtcGxlIiwgbW5fbWFya2VycyRHZW5lLnN0YWJsZS5JRCwgY29scyA9IGMoImRhcmtncmV5IiwgIiM0MTljNzMiKSkKYGBgCgojIyBNRk9McwoKYGBge3J9CmduYW1lc1tncmVwbCgiXlBMUDEkfF5NQlAkfF5GQUJQOSQiLGduYW1lcyRHZW5lLm5hbWUpLF0KCiMgTUZPTCBtYXJrZXIgZXhwcmVzc2lvbgpWbG5QbG90KG15LnNlLCBmZWF0dXJlcyA9IGMoIkVOU0dBTEcwMDAwMDAwMDExMiIsIkVOU0dBTEcwMDAwMDAxMzY0MCIsIkVOU0dBTEcwMDAwMDAzMjQ1MyIpLCBwdC5zaXplID0gMCwgbmNvbCA9IDEpCgojIHZlY3RvciBvZiBuZXVyb25hbCBjbHVzdGVycwpNRk9MX2NsdXN0ZXJzIDwtIGMoMTYsIDE4LCAzNCkKCk1GT0xfbHVtYl9tYXJrZXJzIDwtIGZpbHRlcihsdW1iX21hcmtlcnMsIGNsdXN0X2lkICVpbiUgTUZPTF9jbHVzdGVycykKTUZPTF9sdW1iX251bWJlcnMgPC0gZmlsdGVyKGx1bWJfbnVtYmVycywgY2x1c3RfaWQgJWluJSBNRk9MX2NsdXN0ZXJzKQpNRk9MX2x1bWJfY29tcG9zaXRpb24gPC0gZmlsdGVyKGx1bWJfY29tcG9zaXRpb24sIGNsdXN0X2lkICVpbiUgTUZPTF9jbHVzdGVycykKYGBgCgoKIyMjIEJhcnBsb3RzCgpCYXJwbG90cyBzaG93aW5nIG51bWJlciBvZiBjZWxscyBmcm9tIEIxMCBvciBMMTAsIGFuZCB0aGUgY29udHJpYnV0aW9ucyBmcm9tIHRoZSBpbmRpdmlkdWFsIEIxMGludCBhbmQgTDEwaW50IGNsdXN0ZXJzOgoKYGBge3IgYmFycGxvdHMtTDEwLU1GT0x9CmdncGxvdChkYXRhID0gTUZPTF9sdW1iX251bWJlcnMsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gY2x1c3RlciwgbGFiZWwgPSBGcmVxKSkgKwogIGdlb21fY29sKCkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKwogIGdlb21fdGV4dChudWRnZV95ID0gMTApICsKICBnZ3RpdGxlKCJDbHVzdGVyIGNvbXBvc2l0aW9uIGJ5IHNhbXBsZSAobkNlbGxzKSIpCgpgYGAKCmBgYHtyfQp0b3Bsb3QgPC0gTUZPTF9sdW1iX2NvbXBvc2l0aW9uICU+JSAKICBtdXRhdGUoYW5ub3Rfc2FtcGxlID0gc3RyX2V4dHJhY3QoYW5ub3Rfc2FtcGxlLCAiXFxkezEsMn1fLnsxfSIpKSAlPiUgCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IGZhY3Rvcihhbm5vdF9zYW1wbGUpKSAlPiUgCiAgZ3JvdXBfYnkoc2FtcGxlLCBjbHVzdGVyKSAlPiUKICBjb3VudChhbm5vdF9zYW1wbGUpICU+JSAKICBtdXRhdGUobGFiZWxfeXBvcyA9IGN1bXN1bShuKS0gMC41Km4pCgpCTF9NRk9MX2JhcnBsb3QgPC0gZ2dwbG90KGRhdGE9dG9wbG90LCBhZXMoeD1zYW1wbGUsIHk9biwgZmlsbD1mY3RfcmV2KGFubm90X3NhbXBsZSkpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHk9bGFiZWxfeXBvcywgbGFiZWw9YW5ub3Rfc2FtcGxlKSwgCiAgICAgICAgICAgIGNvbG9yPSJibGFjayIsIHNpemU9My41KSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgZ2d0aXRsZSgiRnJvbSB3aGljaCBjbHVzdGVycyBkbyBjZWxscyBjb21lPyIpCgpCTF9NRk9MX2JhcnBsb3QgCmBgYAoKIyMjIFZvbGNhbm9wbG90cwoKYGBge3Igdm9scGxvdHMtTDEwX01GT0wsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90IDwtIE1GT0xfbHVtYl9tYXJrZXJzICU+JSAKICBtdXRhdGUoZ2VuZV90eXBlID0gY2FzZV93aGVuKAogICAgYXZnX2xvZzJGQyA+PSAwLjUgJiBwX3ZhbF9hZGogPD0gMC4wNSB+ICJjdHJsIiwKICAgIGF2Z19sb2cyRkMgPD0gLTAuNSAmIHBfdmFsX2FkaiA8PSAwLjA1IH4gImx1bWIiLAogICAgVFJVRSB+ICJucyIpCiAgKQoKY29scyA8LSBjKGN0cmwgPSAiYmxhY2siLAogICAgICAgICAgbHVtYiA9ICIjNDE5YzczIiwKICAgICAgICAgIG5zID0gImdyZXkiKQoKc2hhcGVzIDwtIGMoY3RybCA9IDIxLAogICAgICAgICAgICBsdW1iID0gMjEsCiAgICAgICAgICAgIG5zID0gMjApCgp2b2xwbG90IDwtIGdncGxvdChkYXRhID0gdG9wbG90LAogICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3QsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA1MDApCgpgYGAKClBsb3RzIHdpdGhvdXQgSE9YIGFuZCBtaXRvY2hvbmRyaWFsIGdlbmVzOgoKYGBge3IsICBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CiMgZmlsdGVyIG91dCBIT1ggYW5kIE1UIGdlbmVzCnRvcGxvdF9ub210IDwtIHRvcGxvdCAlPiUgCiAgZmlsdGVyKCFncmVwbCgiXkhPWCIsIEdlbmUubmFtZSkpICU+JSAKICBmaWx0ZXIoIUdlbmUuc3RhYmxlLklEICVpbiUgbXQpCgp2b2xwbG90X25vbXQgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3Rfbm9tdCwKICAgICAgICAgICAgICAgICAgYWVzKHggPSBhdmdfbG9nMkZDLAogICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGdlbmVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZV90eXBlCiAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwLmFkaiksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC1sMmZjLGwyZmMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVzKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxLCBzY2FsZXMgPSAiZnJlZSIpICsKICB5bGFiKCItbG9nMTAocGFkaikiKSArCiAgdGhlbWVfYncoKSsKICBnZ3RpdGxlKCJTaWcuIG1hcmtlciBnZW5lcyAod2l0aG91dCBNVCBhbmQgSE9YIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3Rfbm9tdCwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDUwMCkKCmBgYAoKYGBge3J9CnZvbHBsb3Rfbm9tdCArCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gaWZlbHNlKGdlbmVfdHlwZSA9PSAnbnMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5uYW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzRfTUZPTF92b2xjYW5vLnBkZiIsIHdpZHRoID0gNywgaGVpZ2h0ID0gMy41KQp2b2xwbG90X25vbXQgKwogIE5vTGVnZW5kKCkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShnZW5lX3R5cGUgPT0gJ25zJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUubmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKQkxfTUZPTF9iYXJwbG90CmRldi5vZmYoKQpgYGAKCgojIEIxMC1QMTAtaW50CgpgYGB7ciBCMTAtUDEwLWludH0KIyBzZXVyYXQgb2JqZWN0Cm15LnNlIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX3BvbHlfaW50X3NldXJhdF8yNTA3MjMucmRzIikKIyBjbHVzdGVyIGxhYmVscyBmcm9tIEIxMGludCBhbmQgTDEwaW50CmN0cmxfcG9seV9pbnRfY29tYmluZWRfbGFiZWxzIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvYW5ub3RhdGlvbnMvY3RybF9wb2x5X2ludF9jb21iaW5lZF9sYWJlbHMucmRzIikKCmlkZW50aWNhbChjb2xuYW1lcyhteS5zZSksIHJvd25hbWVzKGN0cmxfcG9seV9pbnRfY29tYmluZWRfbGFiZWxzKSkKbXkuc2UkYW5ub3Rfc2FtcGxlICA8LSBjdHJsX3BvbHlfaW50X2NvbWJpbmVkX2xhYmVscyRhbm5vdF9zYW1wbGUKCm15LnNlQGFjdGl2ZS5hc3NheSA8LSAiUk5BIgoKYGBgCgojIyBpbnRyYSBjbHVzdGVyIERFIGFuYWx5c2lzCgpXZSBkbyBERSBhbmFseXNpcyBwZXIgY2x1c3RlciwgY29udHJhc3RpbmcgQjEwIGFuZCBQMTAgc2FtcGxlczoKCmBgYHtyIGludHJhLWNsdXN0ZXItREUtUDEwLCBldmFsPUZBTFNFfQptYXJrZXJzIDwtIGxpc3QoKQpudW1iZXJzIDwtIGxpc3QoKQpjb21wb3NpdGlvbiA8LSBsaXN0KCkKCmZvciAoaSBpbiBzZXEobGV2ZWxzKElkZW50cyhteS5zZSkpKSkgewogICMgc3Vic2V0IGZvciBpbmRpdmlkdWFsIGNsdXN0ZXJzCiAgbW4uc2UgPC0gc3Vic2V0KHggPSBteS5zZSwgaWRlbnRzID0gbGV2ZWxzKElkZW50cyhteS5zZSkpW2ldKQogIG1uLnNlJHNhbXBsZSA8LSBzdHJfZXh0cmFjdChtbi5zZSRvcmlnLmlkZW50LCAiY3RybHxwb2x5IikKICAKICBjb21wb3NpdGlvbltbaV1dIDwtIG1uLnNlW1tdXSAlPiUgCiAgICBzZWxlY3Qoc2FtcGxlLCBhbm5vdF9zYW1wbGUpCiAgCiAgSWRlbnRzKG1uLnNlKSA8LSAic2FtcGxlIgogIAogIHRtcF9tYXJrZXJzIDwtIEZpbmRNYXJrZXJzKG1uLnNlLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlkZW50LjEgPSAiY3RybCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgb25seS5wb3MgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWluLnBjdCA9IDAuMjUsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxvZ2ZjLnRocmVzaG9sZCA9ICAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGF0ZW50LnZhcnMgPSBjKCJDQy5EaWZmZXJlbmNlLnNldXJhdCIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0ZXN0LnVzZSA9ICJNQVNUIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYXNzYXkgPSAiUk5BIikKICAjIGNlbGwgbnVtYmVycyBwZXIgc2FtcGxlCiAgbnVtYmVyc1tbaV1dIDwtIGRhdGEuZnJhbWUodGFibGUobW4uc2Ukc2FtcGxlKSkKICAKICB0bXBfbWFya2VycyA8LSB0bXBfbWFya2VycyAlPiUKICAgIHJvd25hbWVzX3RvX2NvbHVtbigiR2VuZS5zdGFibGUuSUQiKSAlPiUgCiAgICBsZWZ0X2pvaW4oZ25hbWVzKQogIAogIAogIG1hcmtlcnNbW2ldXSA8LSB0bXBfbWFya2Vycwp9CgpuYW1lcyhtYXJrZXJzKSA8LSBwYXN0ZTAoImNsLSIsIGxldmVscyhJZGVudHMobXkuc2UpKSkKbmFtZXMobnVtYmVycykgPC0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpCm5hbWVzKGNvbXBvc2l0aW9uKSA8LSBwYXN0ZTAoImNsLSIsIGxldmVscyhJZGVudHMobXkuc2UpKSkKCiMgYmluZCBsaXN0cyBpbnRvIGRhdGEgZnJhbWVzCnBvbHlfbWFya2VycyA8LSBiaW5kX3Jvd3MobWFya2VycywgLmlkID0gImNsdXN0ZXIiKSAlPiUgCiAgbXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlciwgbGV2ZWxzID0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpKSkKcG9seV9udW1iZXJzIDwtIGJpbmRfcm93cyhudW1iZXJzLCAuaWQgPSAiY2x1c3RlciIpICU+JSAKICBtdXRhdGUoY2x1c3RlciA9IGZhY3RvcihjbHVzdGVyLCBsZXZlbHMgPSBwYXN0ZTAoImNsLSIsIGxldmVscyhJZGVudHMobXkuc2UpKSkpKQpwb2x5X2NvbXBvc2l0aW9uIDwtIGJpbmRfcm93cyhjb21wb3NpdGlvbiwgLmlkID0gImNsdXN0ZXIiKSAlPiUgCiAgbXV0YXRlKGNsdXN0ZXIgPSBmYWN0b3IoY2x1c3RlciwgbGV2ZWxzID0gcGFzdGUwKCJjbC0iLCBsZXZlbHMoSWRlbnRzKG15LnNlKSkpKSkKCnNhdmVSRFMocG9seV9tYXJrZXJzLCAifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfcG9seV9pbnRfbWFya2Vycy5yZHMiKQpzYXZlUkRTKHBvbHlfbnVtYmVycywgIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX3BvbHlfaW50X251bWJlcnMucmRzIikKc2F2ZVJEUyhwb2x5X2NvbXBvc2l0aW9uLCAifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfcG9seV9pbnRfY29tcG9zaXRpb24ucmRzIikKYGBgCgoKYGBge3J9CiMgbG9hZCB0aGUgREUgZGF0YQpwb2x5X21hcmtlcnMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfcG9seV9pbnRfbWFya2Vycy5yZHMiKSAlPiUgCiAgbXV0YXRlKGNsdXN0X2lkID0gc3RyX3JlbW92ZShjbHVzdGVyLCAiXmNsLSIpKQpwb2x5X251bWJlcnMgPC0gcmVhZFJEUygifi9zcGluYWxfY29yZF9wYXBlci9kYXRhL0dnX2N0cmxfcG9seV9pbnRfbnVtYmVycy5yZHMiKSAlPiUgCiAgbXV0YXRlKGNsdXN0X2lkID0gc3RyX3JlbW92ZShjbHVzdGVyLCAiXmNsLSIpKQpwb2x5X2NvbXBvc2l0aW9uIDwtIHJlYWRSRFMoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZGF0YS9HZ19jdHJsX3BvbHlfaW50X2NvbXBvc2l0aW9uLnJkcyIpICU+JSAKICBtdXRhdGUoY2x1c3RfaWQgPSBzdHJfcmVtb3ZlKGNsdXN0ZXIsICJeY2wtIikpCmBgYAoKUGxvdCB0aGUgY2x1c3RlciBjb21wb3NpdGlvbnMgYW5kIHRoZSBudW1iZXIgb2YgbWFya2VyIGdlbmVzLgoKYGBge3J9CgpEaW1QbG90KG15LnNlLCBsYWJlbCA9IFRSVUUsIHJlZHVjdGlvbiA9ICJ0c25lIikKCmdncGxvdChkYXRhID0gcG9seV9udW1iZXJzLCBhZXMoeCA9IFZhcjEsIHkgPSBGcmVxLCBncm91cCA9IGNsdXN0ZXIsIGxhYmVsID0gRnJlcSkpICsKICBnZW9tX2NvbCgpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDMpICsKICBnZW9tX3RleHQobnVkZ2VfeSA9IDUwLCBzaXplID0gMykgKwogIGdndGl0bGUoIkNsdXN0ZXIgY29tcG9zaXRpb24gYnkgc2FtcGxlIChuQ2VsbHMpIikKCmdncGxvdChkYXRhID0gcG9seV9tYXJrZXJzICU+JSBmaWx0ZXIocF92YWxfYWRqIDwgMC4wNSAmIGF2Z19sb2cyRkMgPiAwLjUpLCBhZXMoeCA9IGNsdXN0ZXIsIGdyb3VwID0gY2x1c3RlcikpICsKICBnZW9tX2JhcigpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDMsc2NhbGVzID0gImZyZWVfeCIpICsKICBnZ3RpdGxlKCJOdW1iZXIgb2Ygc2lnLiBERSBnZW5lcyIpCgpgYGAKCiMjIE5ldXJvbnMKCmBgYHtyfQpnbmFtZXNbZ3JlcGwoIlRVQkIzIixnbmFtZXMkR2VuZS5uYW1lKSxdCgojIFRVQkIzIGV4cHJlc3Npb24KVmxuUGxvdChteS5zZSwgZmVhdHVyZXMgPSBjKCJFTlNHQUxHMDAwMDAwMDAwNTkiKSwgcHQuc2l6ZSA9IDApCgojIHZlY3RvciBvZiBuZXVyb25hbCBjbHVzdGVycwpuZXVyb25fY2x1c3RlcnMgPC0gYyg1LDYsMTEsMjAsMjUsMjcsMjksMzAsMzIpCgpuZXVyb25fcG9seV9tYXJrZXJzIDwtIGZpbHRlcihwb2x5X21hcmtlcnMsIGNsdXN0X2lkICVpbiUgbmV1cm9uX2NsdXN0ZXJzKQpuZXVyb25fcG9seV9udW1iZXJzIDwtIGZpbHRlcihwb2x5X251bWJlcnMsIGNsdXN0X2lkICVpbiUgbmV1cm9uX2NsdXN0ZXJzKQpuZXVyb25fcG9seV9jb21wb3NpdGlvbiA8LSBmaWx0ZXIocG9seV9jb21wb3NpdGlvbiwgY2x1c3RfaWQgJWluJSBuZXVyb25fY2x1c3RlcnMpCmBgYAoKCgojIyMgQmFycGxvdHMKCkJhcnBsb3RzIHNob3dpbmcgbnVtYmVyIG9mIGNlbGxzIGZyb20gQjEwIG9yIFAxMCwgYW5kIHRoZSBjb250cmlidXRpb25zIGZyb20gdGhlIGluZGl2aWR1YWwgQjEwaW50IGFuZCBQMTBpbnQgY2x1c3RlcnM6CgpgYGB7ciBiYXJwbG90cy1QMTB9CmdncGxvdChkYXRhID0gbmV1cm9uX3BvbHlfbnVtYmVycywgYWVzKHggPSBWYXIxLCB5ID0gRnJlcSwgZ3JvdXAgPSBjbHVzdGVyLCBsYWJlbCA9IEZyZXEpKSArCiAgZ2VvbV9jb2woKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxKSArCiAgZ2VvbV90ZXh0KG51ZGdlX3kgPSAxMCkgKwogIGdndGl0bGUoIkNsdXN0ZXIgY29tcG9zaXRpb24gYnkgc2FtcGxlIChuQ2VsbHMpIikKCmBgYAoKYGBge3J9Cgp0b3Bsb3QgPC0gbmV1cm9uX3BvbHlfY29tcG9zaXRpb24gJT4lIAogIG11dGF0ZShhbm5vdF9zYW1wbGUgPSBzdHJfZXh0cmFjdChhbm5vdF9zYW1wbGUsICJcXGR7MSwyfV8uezF9IikpICU+JSAKICBtdXRhdGUoYW5ub3Rfc2FtcGxlID0gZmFjdG9yKGFubm90X3NhbXBsZSkpICU+JSAKICBncm91cF9ieShzYW1wbGUsIGNsdXN0ZXIpICU+JQogIGNvdW50KGFubm90X3NhbXBsZSkgJT4lIAogIG11dGF0ZShsYWJlbF95cG9zID0gY3Vtc3VtKG4pLSAwLjUqbikKCkJQX25ldXJvbl9iYXJwbG90IDwtIGdncGxvdChkYXRhPXRvcGxvdCwgYWVzKHg9c2FtcGxlLCB5PW4sIGZpbGw9ZmN0X3Jldihhbm5vdF9zYW1wbGUpKSkgKwogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgY29sb3IgPSAiYmxhY2siKSArCiAgZ2VvbV90ZXh0KGFlcyh5PWxhYmVsX3lwb3MsIGxhYmVsPWFubm90X3NhbXBsZSksIAogICAgICAgICAgICBjb2xvcj0iYmxhY2siLCBzaXplPTMuNSkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKyAKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBnZ3RpdGxlKCJGcm9tIHdoaWNoIGNsdXN0ZXJzIGRvIGNlbGxzIGNvbWU/IikKCkJQX25ldXJvbl9iYXJwbG90IApgYGAKCgojIyMgVm9sY2Fub3Bsb3RzCgpgYGB7ciB2b2xwbG90cy1QMTAsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90IDwtIG5ldXJvbl9wb2x5X21hcmtlcnMgJT4lIAogIG11dGF0ZShnZW5lX3R5cGUgPSBjYXNlX3doZW4oCiAgICBhdmdfbG9nMkZDID49IDAuNSAmIHBfdmFsX2FkaiA8PSAwLjA1IH4gImN0cmwiLAogICAgYXZnX2xvZzJGQyA8PSAtMC41ICYgcF92YWxfYWRqIDw9IDAuMDUgfiAicG9seSIsCiAgICBUUlVFIH4gIm5zIikKICApCgpjb2xzIDwtIGMoY3RybCA9ICJibGFjayIsCiAgICAgICAgICBwb2x5ID0gImdvbGRlbnJvZDMiLAogICAgICAgICAgbnMgPSAiZ3JleSIpCgpzaGFwZXMgPC0gYyhjdHJsID0gMjEsCiAgICAgICAgICAgIHBvbHkgPSAyMSwKICAgICAgICAgICAgbnMgPSAyMCkKCgp2b2xwbG90IDwtIGdncGxvdChkYXRhID0gdG9wbG90LAogICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gYXZnX2xvZzJGQywKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgICAgICApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAocC5hZGopLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtbDJmYyxsMmZjKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IHNoYXBlcykgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMiwgc2NhbGVzID0gImZyZWUiKSArCiAgeWxhYigiLWxvZzEwKHBhZGopIikgKwogIHRoZW1lX2J3KCkrCiAgZ2d0aXRsZSgiU2lnLiBtYXJrZXIgZ2VuZXMiKQoKZ2dwbG90bHkodm9scGxvdCwgd2lkdGggPSAxMDAwLCBoZWlnaHQgPSA2MDApCmBgYAoKClBsb3RzIHdpdGhvdXQgbWl0b2Nob25kcmlhbCBnZW5lczoKCmBgYHtyIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90X25vbXQgPC0gdG9wbG90ICU+JSAKICBmaWx0ZXIoIUdlbmUuc3RhYmxlLklEICVpbiUgbXQpCgp2b2xwbG90X25vbXQgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3Rfbm9tdCwKICAgICAgICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSAtbG9nMTAocF92YWxfYWRqKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGdlbmVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZV90eXBlCiAgICAgICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDIsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzICh3aXRob3V0IE1UIGFuZCBIT1ggZ2VuZXMiKQoKZ2dwbG90bHkodm9scGxvdF9ub210KQpgYGAKCmBgYHtyfQp2b2xwbG90X25vbXQgICsKICAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBpZmVsc2UoZ2VuZV90eXBlID09ICducycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLm5hbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfNV9uZXVyb25fdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA3KQp2b2xwbG90X25vbXQgKwogIE5vTGVnZW5kKCkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShnZW5lX3R5cGUgPT0gJ25zJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUubmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKQlBfbmV1cm9uX2JhcnBsb3QgCmRldi5vZmYoKQpgYGAKCgpTaW5nbGUgcGxvdCAobm8gZmFjZXRpbmcgZHVlIHRvIGxvdyBudW1iZXIgb2YgREUgZ2VuZXMpLgoKYGBge3IgZmlnLndpZHRoPTUsIGZpZy5oZWlnaHQ9NX0Kdm9scGxvdF9ub210X2luZCA8LSBnZ3Bsb3QoZGF0YSA9IHRvcGxvdF9ub210LAogICAgICAgICAgICAgICAgICAgICAgIGFlcyh4ID0gYXZnX2xvZzJGQywKICAgICAgICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBnZW5lX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gY2x1c3RlciwKICAgICAgICAgICAgICAgICAgICAgICApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAocC5hZGopLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtbDJmYyxsMmZjKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJibGFjayIsICJncmV5IiwgImdvbGRlbnJvZDMiKSkrCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMCwgMTYsIDIsIDMsIDUsIDE2LCAxNiwgMTYsIDE2KSkgKwogIHhsaW0oYygtMSwxKSkgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpICsKICAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBpZmVsc2UoZ2VuZV90eXBlID09ICducycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLm5hbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCgp2b2xwbG90X25vbXRfaW5kCgpgYGAKCiMjIE1GT0xzCgpgYGB7cn0KZ25hbWVzW2dyZXBsKCJeUExQMSR8Xk1CUCR8XkZBQlA5JCIsZ25hbWVzJEdlbmUubmFtZSksXQoKIyBNRk9MIG1hcmtlciBleHByZXNzaW9uClZsblBsb3QobXkuc2UsIGZlYXR1cmVzID0gYygiRU5TR0FMRzAwMDAwMDAwMTEyIiwiRU5TR0FMRzAwMDAwMDEzNjQwIiwiRU5TR0FMRzAwMDAwMDMyNDUzIiksIHB0LnNpemUgPSAwLCBuY29sID0gMSkKCiMgdmVjdG9yIG9mIG5ldXJvbmFsIGNsdXN0ZXJzCk1GT0xfY2x1c3RlcnMgPC0gYygxMCwgMjQpCgpNRk9MX3BvbHlfbWFya2VycyA8LSBmaWx0ZXIocG9seV9tYXJrZXJzLCBjbHVzdF9pZCAlaW4lIE1GT0xfY2x1c3RlcnMpCk1GT0xfcG9seV9udW1iZXJzIDwtIGZpbHRlcihwb2x5X251bWJlcnMsIGNsdXN0X2lkICVpbiUgTUZPTF9jbHVzdGVycykKTUZPTF9wb2x5X2NvbXBvc2l0aW9uIDwtIGZpbHRlcihwb2x5X2NvbXBvc2l0aW9uLCBjbHVzdF9pZCAlaW4lIE1GT0xfY2x1c3RlcnMpCmBgYAoKIyMjIEJhcnBsb3RzCgpCYXJwbG90cyBzaG93aW5nIG51bWJlciBvZiBjZWxscyBmcm9tIEIxMCBvciBMMTAsIGFuZCB0aGUgY29udHJpYnV0aW9ucyBmcm9tIHRoZSBpbmRpdmlkdWFsIEIxMGludCBhbmQgTDEwaW50IGNsdXN0ZXJzOgoKYGBge3IgYmFycGxvdHMtUDEwLU1GT0x9CmdncGxvdChkYXRhID0gTUZPTF9wb2x5X251bWJlcnMsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gY2x1c3RlciwgbGFiZWwgPSBGcmVxKSkgKwogIGdlb21fY29sKCkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKwogIGdlb21fdGV4dChudWRnZV95ID0gMTApICsKICBnZ3RpdGxlKCJDbHVzdGVyIGNvbXBvc2l0aW9uIGJ5IHNhbXBsZSAobkNlbGxzKSIpCgpgYGAKCmBgYHtyfQp0b3Bsb3QgPC0gTUZPTF9wb2x5X2NvbXBvc2l0aW9uICU+JSAKICBtdXRhdGUoYW5ub3Rfc2FtcGxlID0gc3RyX2V4dHJhY3QoYW5ub3Rfc2FtcGxlLCAiXFxkezEsMn1fLnsxfSIpKSAlPiUgCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IGZhY3Rvcihhbm5vdF9zYW1wbGUpKSAlPiUgCiAgZ3JvdXBfYnkoc2FtcGxlLCBjbHVzdGVyKSAlPiUKICBjb3VudChhbm5vdF9zYW1wbGUpICU+JSAKICBtdXRhdGUobGFiZWxfeXBvcyA9IGN1bXN1bShuKS0gMC41Km4pCgpCUF9NRk9MX2JhcnBsb3QgPC0gZ2dwbG90KGRhdGE9dG9wbG90LCBhZXMoeD1zYW1wbGUsIHk9biwgZmlsbD1mY3RfcmV2KGFubm90X3NhbXBsZSkpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHk9bGFiZWxfeXBvcywgbGFiZWw9YW5ub3Rfc2FtcGxlKSwgCiAgICAgICAgICAgIGNvbG9yPSJibGFjayIsIHNpemU9My41KSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgZ2d0aXRsZSgiRnJvbSB3aGljaCBjbHVzdGVycyBkbyBjZWxscyBjb21lPyIpCgpCUF9NRk9MX2JhcnBsb3QgCmBgYAoKIyMjIFZvbGNhbm9wbG90cwoKYGBge3Igdm9scGxvdHMtUDEwX01GT0wsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90IDwtIE1GT0xfcG9seV9tYXJrZXJzICU+JSAKICBtdXRhdGUoZ2VuZV90eXBlID0gY2FzZV93aGVuKAogICAgYXZnX2xvZzJGQyA+PSAwLjUgJiBwX3ZhbF9hZGogPD0gMC4wNSB+ICJjdHJsIiwKICAgIGF2Z19sb2cyRkMgPD0gLTAuNSAmIHBfdmFsX2FkaiA8PSAwLjA1IH4gInBvbHkiLAogICAgVFJVRSB+ICJucyIpCiAgKQoKY29scyA8LSBjKGN0cmwgPSAiYmxhY2siLAogICAgICAgICAgcG9seSA9ICJnb2xkZW5yb2QzIiwKICAgICAgICAgIG5zID0gImdyZXkiKQoKc2hhcGVzIDwtIGMoY3RybCA9IDIxLAogICAgICAgICAgICBwb2x5ID0gMjEsCiAgICAgICAgICAgIG5zID0gMjApCgp2b2xwbG90IDwtIGdncGxvdChkYXRhID0gdG9wbG90LAogICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3QsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA1MDApCgpgYGAKClBsb3RzIHdpdGhvdXQgSE9YIGFuZCBtaXRvY2hvbmRyaWFsIGdlbmVzOgoKYGBge3IsICBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CiMgZmlsdGVyIG91dCBIT1ggYW5kIE1UIGdlbmVzCnRvcGxvdF9ub210IDwtIHRvcGxvdCAlPiUgCiAgZmlsdGVyKCFncmVwbCgiXkhPWCIsIEdlbmUubmFtZSkpICU+JSAKICBmaWx0ZXIoIUdlbmUuc3RhYmxlLklEICVpbiUgbXQpCgp2b2xwbG90X25vbXQgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3Rfbm9tdCwKICAgICAgICAgICAgICAgICAgYWVzKHggPSBhdmdfbG9nMkZDLAogICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGdlbmVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZV90eXBlCiAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwLmFkaiksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC1sMmZjLGwyZmMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVzKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxLCBzY2FsZXMgPSAiZnJlZSIpICsKICB5bGFiKCItbG9nMTAocGFkaikiKSArCiAgdGhlbWVfYncoKSsKICBnZ3RpdGxlKCJTaWcuIG1hcmtlciBnZW5lcyAod2l0aG91dCBNVCBhbmQgSE9YIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3Rfbm9tdCwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDUwMCkKCmBgYAoKYGBge3J9CnZvbHBsb3Rfbm9tdCArCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gaWZlbHNlKGdlbmVfdHlwZSA9PSAnbnMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5uYW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzVfTUZPTF92b2xjYW5vLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMy41KQp2b2xwbG90X25vbXQgKwogIE5vTGVnZW5kKCkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShnZW5lX3R5cGUgPT0gJ25zJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUubmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKQlBfTUZPTF9iYXJwbG90CmRldi5vZmYoKQpgYGAKCiMjIFJQIGFuZCBGUAoKYGBge3J9CmduYW1lc1tncmVwbCgiXlJTUE8xJHxeU0hIJCIsZ25hbWVzJEdlbmUubmFtZSksXQoKIyBNRk9MIG1hcmtlciBleHByZXNzaW9uClZsblBsb3QobXkuc2UsIGZlYXR1cmVzID0gYygiRU5TR0FMRzAwMDAwMDAxOTQ2IiwiRU5TR0FMRzAwMDAwMDA2Mzc5IiksIHB0LnNpemUgPSAwLCBuY29sID0gMSkKCiMgdmVjdG9yIG9mIG5ldXJvbmFsIGNsdXN0ZXJzClJQRlBfY2x1c3RlcnMgPC0gYygyMiwgMjMpCgpSUEZQX3BvbHlfbWFya2VycyA8LSBmaWx0ZXIocG9seV9tYXJrZXJzLCBjbHVzdF9pZCAlaW4lIFJQRlBfY2x1c3RlcnMpClJQRlBfcG9seV9udW1iZXJzIDwtIGZpbHRlcihwb2x5X251bWJlcnMsIGNsdXN0X2lkICVpbiUgUlBGUF9jbHVzdGVycykKUlBGUF9wb2x5X2NvbXBvc2l0aW9uIDwtIGZpbHRlcihwb2x5X2NvbXBvc2l0aW9uLCBjbHVzdF9pZCAlaW4lIFJQRlBfY2x1c3RlcnMpCmBgYAoKIyMjIEJhcnBsb3RzCgpCYXJwbG90cyBzaG93aW5nIG51bWJlciBvZiBjZWxscyBmcm9tIEIxMCBvciBMMTAsIGFuZCB0aGUgY29udHJpYnV0aW9ucyBmcm9tIHRoZSBpbmRpdmlkdWFsIEIxMGludCBhbmQgTDEwaW50IGNsdXN0ZXJzOgoKYGBge3IgYmFycGxvdHMtUDEwLVJQRlB9CmdncGxvdChkYXRhID0gUlBGUF9wb2x5X251bWJlcnMsIGFlcyh4ID0gVmFyMSwgeSA9IEZyZXEsIGdyb3VwID0gY2x1c3RlciwgbGFiZWwgPSBGcmVxKSkgKwogIGdlb21fY29sKCkgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSkgKwogIGdlb21fdGV4dChudWRnZV95ID0gMTApICsKICBnZ3RpdGxlKCJDbHVzdGVyIGNvbXBvc2l0aW9uIGJ5IHNhbXBsZSAobkNlbGxzKSIpCgpgYGAKCmBgYHtyfQp0b3Bsb3QgPC0gUlBGUF9wb2x5X2NvbXBvc2l0aW9uICU+JSAKICBtdXRhdGUoYW5ub3Rfc2FtcGxlID0gc3RyX2V4dHJhY3QoYW5ub3Rfc2FtcGxlLCAiXFxkezEsMn1fLnsxfSIpKSAlPiUgCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IGZhY3Rvcihhbm5vdF9zYW1wbGUpKSAlPiUgCiAgZ3JvdXBfYnkoc2FtcGxlLCBjbHVzdGVyKSAlPiUKICBjb3VudChhbm5vdF9zYW1wbGUpICU+JSAKICBtdXRhdGUobGFiZWxfeXBvcyA9IGN1bXN1bShuKS0gMC41Km4pCgpCUF9SUEZQX2JhcnBsb3QgPC0gZ2dwbG90KGRhdGE9dG9wbG90LCBhZXMoeD1zYW1wbGUsIHk9biwgZmlsbD1mY3RfcmV2KGFubm90X3NhbXBsZSkpKSArCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBjb2xvciA9ICJibGFjayIpICsKICBnZW9tX3RleHQoYWVzKHk9bGFiZWxfeXBvcywgbGFiZWw9YW5ub3Rfc2FtcGxlKSwgCiAgICAgICAgICAgIGNvbG9yPSJibGFjayIsIHNpemU9My41KSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb249Im5vbmUiKSArCiAgZ2d0aXRsZSgiRnJvbSB3aGljaCBjbHVzdGVycyBkbyBjZWxscyBjb21lPyIpCgpCUF9SUEZQX2JhcnBsb3QgCmBgYAoKIyMjIFZvbGNhbm9wbG90cwoKYGBge3Igdm9scGxvdHMtUDEwX1JQRlAsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90IDwtIFJQRlBfcG9seV9tYXJrZXJzICU+JSAKICBtdXRhdGUoZ2VuZV90eXBlID0gY2FzZV93aGVuKAogICAgYXZnX2xvZzJGQyA+PSAwLjUgJiBwX3ZhbF9hZGogPD0gMC4wNSB+ICJjdHJsIiwKICAgIGF2Z19sb2cyRkMgPD0gLTAuNSAmIHBfdmFsX2FkaiA8PSAwLjA1IH4gInBvbHkiLAogICAgVFJVRSB+ICJucyIpCiAgKQoKY29scyA8LSBjKGN0cmwgPSAiYmxhY2siLAogICAgICAgICAgcG9seSA9ICJnb2xkZW5yb2QzIiwKICAgICAgICAgIG5zID0gImdyZXkiKQoKc2hhcGVzIDwtIGMoY3RybCA9IDIxLAogICAgICAgICAgICBwb2x5ID0gMjEsCiAgICAgICAgICAgIG5zID0gMjApCgp2b2xwbG90IDwtIGdncGxvdChkYXRhID0gdG9wbG90LAogICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3QsIHdpZHRoID0gODAwLCBoZWlnaHQgPSA1MDApCgpgYGAKClBsb3RzIHdpdGhvdXQgSE9YIGFuZCBtaXRvY2hvbmRyaWFsIGdlbmVzOgoKYGBge3IsICBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDZ9CiMgZmlsdGVyIG91dCBIT1ggYW5kIE1UIGdlbmVzCnRvcGxvdF9ub210IDwtIHRvcGxvdCAlPiUgCiAgZmlsdGVyKCFncmVwbCgiXkhPWCIsIEdlbmUubmFtZSkpICU+JSAKICBmaWx0ZXIoIUdlbmUuc3RhYmxlLklEICVpbiUgbXQpCgp2b2xwbG90X25vbXQgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3Rfbm9tdCwKICAgICAgICAgICAgICAgICAgYWVzKHggPSBhdmdfbG9nMkZDLAogICAgICAgICAgICAgICAgICAgICAgeSA9IC1sb2cxMChwX3ZhbF9hZGopLAogICAgICAgICAgICAgICAgICAgICAgbGFiZWwgPSBHZW5lLm5hbWUsCiAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGdlbmVfdHlwZSwKICAgICAgICAgICAgICAgICAgICAgIHNoYXBlID0gZ2VuZV90eXBlCiAgICAgICAgICAgICAgICAgICkpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChwLmFkaiksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBjKC1sMmZjLGwyZmMpLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGNvbHMpICsKICBzY2FsZV9zaGFwZV9tYW51YWwodmFsdWVzID0gc2hhcGVzKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxLCBzY2FsZXMgPSAiZnJlZSIpICsKICB5bGFiKCItbG9nMTAocGFkaikiKSArCiAgdGhlbWVfYncoKSsKICBnZ3RpdGxlKCJTaWcuIG1hcmtlciBnZW5lcyAod2l0aG91dCBNVCBhbmQgSE9YIGdlbmVzIikKCmdncGxvdGx5KHZvbHBsb3Rfbm9tdCwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDUwMCkKCmBgYAoKYGBge3J9CnZvbHBsb3Rfbm9tdCArCiAgICBnZ3JlcGVsOjpnZW9tX3RleHRfcmVwZWwoYWVzKGxhYmVsID0gaWZlbHNlKGdlbmVfdHlwZSA9PSAnbnMnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBOQSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgR2VuZS5uYW1lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSAiYmxhY2siKQoKcGRmKCJ+L3NwaW5hbF9jb3JkX3BhcGVyL2ZpZ3VyZXMvRmlnXzVfUlBGUF92b2xjYW5vLnBkZiIsIHdpZHRoID0gNSwgaGVpZ2h0ID0gMy41KQp2b2xwbG90X25vbXQgKwogIE5vTGVnZW5kKCkgKwogICAgZ2dyZXBlbDo6Z2VvbV90ZXh0X3JlcGVsKGFlcyhsYWJlbCA9IGlmZWxzZShnZW5lX3R5cGUgPT0gJ25zJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTkEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIEdlbmUubmFtZSkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsYWNrIikKQlBfUlBGUF9iYXJwbG90CmRldi5vZmYoKQpgYGAKCiMjIE5QQyBjbC0xNiBhbmQgY2wtMTgKCkNsdXN0ZXJzIDE2IGFuZCAxOCBzaG93IHRoZSBoaWdoZXN0IG51bWJlcnMgb2YgREUgZ2VuZXMuIFRoZXkgYXJlIGEgcHJvZ2VuaXRvciBwb3B1bGF0aW9uCgpgYGB7cn0KIyB2ZWN0b3Igb2YgbmV1cm9uYWwgY2x1c3RlcnMKTlBDX2NsdXN0ZXJzIDwtIGMoMTYsIDE4KQoKTlBDX3BvbHlfbWFya2VycyA8LSBmaWx0ZXIocG9seV9tYXJrZXJzLCBjbHVzdF9pZCAlaW4lIE5QQ19jbHVzdGVycykKTlBDX3BvbHlfbnVtYmVycyA8LSBmaWx0ZXIocG9seV9udW1iZXJzLCBjbHVzdF9pZCAlaW4lIE5QQ19jbHVzdGVycykKTlBDX3BvbHlfY29tcG9zaXRpb24gPC0gZmlsdGVyKHBvbHlfY29tcG9zaXRpb24sIGNsdXN0X2lkICVpbiUgTlBDX2NsdXN0ZXJzKQpgYGAKCiMjIyBCYXJwbG90cwoKQmFycGxvdHMgc2hvd2luZyBudW1iZXIgb2YgY2VsbHMgZnJvbSBCMTAgb3IgTDEwLCBhbmQgdGhlIGNvbnRyaWJ1dGlvbnMgZnJvbSB0aGUgaW5kaXZpZHVhbCBCMTBpbnQgYW5kIEwxMGludCBjbHVzdGVyczoKCmBgYHtyIGJhcnBsb3RzLVAxMC1OUEN9CmdncGxvdChkYXRhID0gTlBDX3BvbHlfbnVtYmVycywgYWVzKHggPSBWYXIxLCB5ID0gRnJlcSwgZ3JvdXAgPSBjbHVzdGVyLCBsYWJlbCA9IEZyZXEpKSArCiAgZ2VvbV9jb2woKSArCiAgZmFjZXRfd3JhcCgiY2x1c3RlciIsIG5yb3cgPSAxKSArCiAgZ2VvbV90ZXh0KG51ZGdlX3kgPSAxMCkgKwogIGdndGl0bGUoIkNsdXN0ZXIgY29tcG9zaXRpb24gYnkgc2FtcGxlIChuQ2VsbHMpIikKCmBgYAoKYGBge3J9CnRvcGxvdCA8LSBOUENfcG9seV9jb21wb3NpdGlvbiAlPiUgCiAgbXV0YXRlKGFubm90X3NhbXBsZSA9IHN0cl9leHRyYWN0KGFubm90X3NhbXBsZSwgIlxcZHsxLDJ9Xy57MX0iKSkgJT4lIAogIG11dGF0ZShhbm5vdF9zYW1wbGUgPSBmYWN0b3IoYW5ub3Rfc2FtcGxlKSkgJT4lIAogIGdyb3VwX2J5KHNhbXBsZSwgY2x1c3RlcikgJT4lCiAgY291bnQoYW5ub3Rfc2FtcGxlKSAlPiUgCiAgbXV0YXRlKGxhYmVsX3lwb3MgPSBjdW1zdW0obiktIDAuNSpuKQoKQlBfTlBDX2JhcnBsb3QgPC1nZ3Bsb3QoZGF0YT10b3Bsb3QsIGFlcyh4PXNhbXBsZSwgeT1uLCBmaWxsPWZjdF9yZXYoYW5ub3Rfc2FtcGxlKSkpICsKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fdGV4dChhZXMoeT1sYWJlbF95cG9zLCBsYWJlbD1hbm5vdF9zYW1wbGUpLCAKICAgICAgICAgICAgY29sb3I9ImJsYWNrIiwgc2l6ZT0zLjUpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDEpICsKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsKICBnZ3RpdGxlKCJGcm9tIHdoaWNoIGNsdXN0ZXJzIGRvIGNlbGxzIGNvbWU/IikKCkJQX05QQ19iYXJwbG90IApgYGAKCiMjIyBWb2xjYW5vcGxvdHMKCmBgYHtyIHZvbHBsb3RzLVAxMF9OUEMsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KdG9wbG90IDwtIE5QQ19wb2x5X21hcmtlcnMgJT4lIAogIG11dGF0ZShnZW5lX3R5cGUgPSBjYXNlX3doZW4oCiAgICBhdmdfbG9nMkZDID49IDAuNSAmIHBfdmFsX2FkaiA8PSAwLjA1IH4gImN0cmwiLAogICAgYXZnX2xvZzJGQyA8PSAtMC41ICYgcF92YWxfYWRqIDw9IDAuMDUgfiAicG9seSIsCiAgICBUUlVFIH4gIm5zIikKICApCgpjb2xzIDwtIGMoY3RybCA9ICJibGFjayIsCiAgICAgICAgICBwb2x5ID0gImdvbGRlbnJvZDMiLAogICAgICAgICAgbnMgPSAiZ3JleSIpCgpzaGFwZXMgPC0gYyhjdHJsID0gMjEsCiAgICAgICAgICAgIHBvbHkgPSAyMSwKICAgICAgICAgICAgbnMgPSAyMCkKCnZvbHBsb3QgPC0gZ2dwbG90KGRhdGEgPSB0b3Bsb3QsCiAgICAgICAgICAgICAgICAgIGFlcyh4ID0gYXZnX2xvZzJGQywKICAgICAgICAgICAgICAgICAgICAgIHkgPSAtbG9nMTAocF92YWxfYWRqKSwKICAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gR2VuZS5uYW1lLAogICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSBnZW5lX3R5cGUsCiAgICAgICAgICAgICAgICAgICAgICBzaGFwZSA9IGdlbmVfdHlwZQogICAgICAgICAgICAgICAgICApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAocC5hZGopLCBsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gYygtbDJmYyxsMmZjKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjb2xzKSArCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IHNoYXBlcykgKwogIGZhY2V0X3dyYXAoImNsdXN0ZXIiLCBucm93ID0gMSwgc2NhbGVzID0gImZyZWUiKSArCiAgeWxhYigiLWxvZzEwKHBhZGopIikgKwogIHRoZW1lX2J3KCkrCiAgZ2d0aXRsZSgiU2lnLiBtYXJrZXIgZ2VuZXMiKQoKZ2dwbG90bHkodm9scGxvdCwgd2lkdGggPSA4MDAsIGhlaWdodCA9IDUwMCkKCmBgYAoKUGxvdHMgd2l0aG91dCBIT1ggYW5kIG1pdG9jaG9uZHJpYWwgZ2VuZXM6CgpgYGB7ciwgIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gNn0KIyBmaWx0ZXIgb3V0IEhPWCBhbmQgTVQgZ2VuZXMKdG9wbG90X25vbXQgPC0gdG9wbG90ICU+JSAKICBmaWx0ZXIoIWdyZXBsKCJeSE9YIiwgR2VuZS5uYW1lKSkgJT4lIAogIGZpbHRlcighR2VuZS5zdGFibGUuSUQgJWluJSBtdCkKCnZvbHBsb3Rfbm9tdCA8LSBnZ3Bsb3QoZGF0YSA9IHRvcGxvdF9ub210LAogICAgICAgICAgICAgICAgICBhZXMoeCA9IGF2Z19sb2cyRkMsCiAgICAgICAgICAgICAgICAgICAgICB5ID0gLWxvZzEwKHBfdmFsX2FkaiksCiAgICAgICAgICAgICAgICAgICAgICBsYWJlbCA9IEdlbmUubmFtZSwKICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gZ2VuZV90eXBlLAogICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBnZW5lX3R5cGUKICAgICAgICAgICAgICAgICAgKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKHAuYWRqKSwgbGluZXR5cGUgPSAiZGFzaGVkIikgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGMoLWwyZmMsbDJmYyksIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY29scykgKwogIHNjYWxlX3NoYXBlX21hbnVhbCh2YWx1ZXMgPSBzaGFwZXMpICsKICBmYWNldF93cmFwKCJjbHVzdGVyIiwgbnJvdyA9IDEsIHNjYWxlcyA9ICJmcmVlIikgKwogIHlsYWIoIi1sb2cxMChwYWRqKSIpICsKICB0aGVtZV9idygpKwogIGdndGl0bGUoIlNpZy4gbWFya2VyIGdlbmVzICh3aXRob3V0IE1UIGFuZCBIT1ggZ2VuZXMiKQoKZ2dwbG90bHkodm9scGxvdF9ub210LCB3aWR0aCA9IDgwMCwgaGVpZ2h0ID0gNTAwKQoKYGBgCgpgYGB7cn0Kdm9scGxvdF9ub210ICsKICAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBpZmVsc2UoZ2VuZV90eXBlID09ICducycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLm5hbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCgpwZGYoIn4vc3BpbmFsX2NvcmRfcGFwZXIvZmlndXJlcy9GaWdfNV9OUENfdm9sY2Fuby5wZGYiLCB3aWR0aCA9IDUsIGhlaWdodCA9IDMuNSkKdm9scGxvdF9ub210ICsKICBOb0xlZ2VuZCgpICsKICAgIGdncmVwZWw6Omdlb21fdGV4dF9yZXBlbChhZXMobGFiZWwgPSBpZmVsc2UoZ2VuZV90eXBlID09ICducycsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIE5BLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBHZW5lLm5hbWUpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJibGFjayIpCkJQX05QQ19iYXJwbG90CmRldi5vZmYoKQpgYGAKCmBgYHtyfQojIERhdGUgYW5kIHRpbWUgb2YgUmVuZGVyaW5nClN5cy50aW1lKCkKCnNlc3Npb25JbmZvKCkKYGBg